package SRAM(wrapSRAM, mkWrapSRAM) where
import Vector
import FIFOF
import Counter
import Connectable
import GetPut
import ClientServer
import ConfigReg
import RegTwo
import RAM
import SyncSRAM

--@ \subsubsection{\te{SRAM} and \te{TSRAM}}
--@ \index{SRAM@\te{SRAM} (package)|textbf}
--@
--@ The \te{SRAM} package contains functions for wrapping a raw SRAM so that
--@ it has the more convenient RAM interface.

--@ The \te{mkWrapSRAM} function takes a \te{SyncSRAM} module and turns it into
--@ a \te{RAM} module.
--@ \index{mkWrapSRAM@\te{mkWrapSRAM} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkWrapSRAM#(Module#(SyncSRAMS#(lat, adrs, dtas)) mkRam)(RAM#(adr, dta))
--@   provisos (Bits#(adr, adrs),
--@             Bits#(dta, dtas),
--@             Add#(1, lat, lat1),
--@             Add#(4, lat, lat4),
--@             Log#(lat4, llat));
--@ \end{libverbatim}
mkWrapSRAM :: (IsModule m c, Bits adr adrs, Bits dta dtas, Add 1 lat lat1, Add 4 lat lat4, Log lat4 llat) =>
              m (SyncSRAMS lat adrs dtas) -> m (RAM adr dta)
mkWrapSRAM mkRam =
  do
    sram :: SyncSRAMS lat adrs dtas <- mkRam
    (cram, ram) :: (SyncSRAMC lat adrs dtas, RAM adr dta) <- wrapSRAM
    sram <-> cram
    return ram

--@ The \te{wrapSRAM} module generates a \te{SyncSRAMC} client and
--@ a \te{RAM} server.  The client interface can be exported and hooked
--@ up to an external SRAM, or hooked up to an internally generated SRAM.
--@ \index{wrapSRAM@\te{wrapSRAM} (module)|textbf}
--@ \begin{libverbatim}
--@ module wrapSRAM(Tuple2 #(SyncSRAMC#(lat, adrs, dtas), RAM#(adr, dta)))
--@   provisos (Bits#(adr, adrs),
--@             Bits#(dta, dtas),
--@             Add#(1, lat, lat1),
--@             Add#(4, lat, lat4),
--@             Log#(lat4, llat));
--@ \end{libverbatim}
wrapSRAM :: (IsModule m c, Bits adr adrs, Bits dta dtas, Add 1 lat lat1, Add 4 lat lat4, Log lat4 llat) =>
            m (SyncSRAMC lat adrs dtas, RAM adr dta)
wrapSRAM =
  module
    let lat = fromInteger (valueOf lat)
    out :: FIFOF dta           <- mkUGSizedFIFOF (lat + 4)    -- output FIFO
    adr :: Reg (Bit adrs)      <- mkConfigRegU                -- SRAM address reg
    dta :: Reg (Bit dtas)      <- mkConfigRegU                -- SRAM data reg
    wen :: Reg (Bit 1)         <- mkConfigReg 0               -- SRAM write enable
    act :: RegTwo (Bit 1)      <- mkRegTwo 0                  -- has request
    obf :: Reg (Bit dtas)      <- mkConfigRegU                -- SRAM output reg
    cnt :: Counter llat        <- mkCounter (lat + 4)         -- free read slots
    rds :: ShiftReg lat1 Bool  <- mkShiftReg False            -- read requests in SRAM

    let hasSpace = cnt.value > 0

    rules
          {-# ASSERT no implicit conditions #-}
          {-# ASSERT fire when enabled #-}
          "SRAMtick":
            when True
             ==> action
                    -- Move the shift register with read flags.
                    rds.shift (act.get == 1 && wen == 0)
                    -- Reset active signal; setA has precedence
                    act.setB 0

          {-# ASSERT no implicit conditions #-}
          {-# ASSERT fire when enabled #-}
          -- Save read values.
          "enq":
            when rds.output
             ==> out.enq (unpack obf)

    interface -- Pair
       (interface Client
            request =
             interface Get
              get = return $
                interface SyncSRAMrequest
                    addr  = adr
                    wdata = dta
                    we    = wen
                    ena   = act.get
            response =
             interface Put
              put res = obf := res
        ,
        interface Server
            request =
             interface Put
              put req = do
                  act.setA 1
                  adr := (pack $ case req of
                                  Read a -> a
                                  Write (address, _) -> address
                         )

                  dta := (pack $ case req of
                                  Read _ -> _
                                  Write (_, value) -> value
                         )

                  wen := (case req of
                           Read _ -> 0
                           Write _ -> 1
                         )

                  cnt.dec (case req of
                              Read _ -> 1
                              Write _ -> 0
                          )

               when hasSpace
            response =
             interface Get
              get = do
                    cnt.up
                    out.deq
                    return out.first
                when out.notEmpty
           )


interface (ShiftReg :: # -> * -> *) n a =
    output :: a
    shift  :: a -> Action

mkShiftReg :: (IsModule m c, Add 1 n1 n, Bits a sa) => a -> m (ShiftReg n a)
mkShiftReg i =
  module
    sr :: Reg (Vector n a) <- mkReg (map (const i) genList)
    interface -- ShiftReg
            output = last sr
            shift s = sr := (s :> init sr)

--@ Both the \te{mkWrapSRAM} and \te{wrapSRAM} modules add two cycles of
--@ latency to the SRAM latency.  The reason for this is that the raw interface
--@ to the SRAM has fully ``registered'' inputs and outputs (which is necessary
--@ for many SRAMs).
--@
--@ \note{The current implementation of these functions is broken, it adds three
--@ extra cycles of latency.}
